package com.google.code.jira.script.listener;

import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Category;

import com.atlassian.jira.event.AbstractEvent;
import com.atlassian.jira.event.JiraListener;

public abstract class ScriptListener implements JiraListener {

	private static final Category log = Category.getInstance(ScriptListener.class);

	private static final String FILE_PARAM = "FILE";

	private static final String ENGINE_PARAM = "ENGINE";

	private String inputFile;

	private String engineName;

	private ScriptEngine scriptEngine;

	public String[] getAcceptedParams() {
		return new String[] { FILE_PARAM, ENGINE_PARAM };
	}

	public String getDescription() {
		return null;
	}

	public void init(Map params) {
		inputFile = (String) params.get(FILE_PARAM);
		engineName = (String) params.get(ENGINE_PARAM);

		// Validates "inputFile" property
		if (inputFile == null || "".equals(inputFile)) {
			log.error("Skip running listener because input file is NULL!");
			scriptEngine = null;
		}

		ScriptEngineManager manager = new ScriptEngineManager();
		if (engineName == null) {
			String extension = FilenameUtils.getExtension(inputFile);
			if (extension != null && extension.length() > 1) {
				scriptEngine = manager.getEngineByExtension(extension.substring(1));
				engineName = (String) scriptEngine.get(ScriptEngine.ENGINE);
			}
		}
	}

	public boolean isInternal() {
		return false;
	}

	public boolean isUnique() {
		return false;
	}

	public void run(String eventName, AbstractEvent event) {
		log.info("start [script=" + inputFile + "; engine=" + engineName + "]");

		// Validates "engineName" property
		if (engineName == null || "".equals(engineName)) {
			log.error("Engine name can't be NULL!");
			return;
		}

		// Validates "inputFile" property
		if (inputFile == null || "".equals(inputFile)) {
			log.error("Input file can't be NULL!");
			return;
		}

		// Loads the engine
		ScriptEngineManager manager = new ScriptEngineManager();
		List factories = manager.getEngineFactories();
		if (factories == null || factories.size() == 0) {
			log.error("No engines found! Is JSR-233 supported?");
		}

		ScriptEngine engine = null;
		for (Iterator iterator = factories.iterator(); iterator.hasNext();) {
			ScriptEngineFactory factory = (ScriptEngineFactory) iterator.next();
			if (engineName.equals(factory.getEngineName())) {
				engine = factory.getScriptEngine();
				break;
			}
		}

		if (engine == null) {
			log.error("No script engine found associate with the name " + engineName + ".");
		}

		Reader reader = null;
		try {
			reader = new FileReader(inputFile);
			engine.eval(reader);
			engine.put("log", log);

			Invocable invocable = (Invocable) engine;
			invocable.invokeFunction(eventName, new Object[] { event });
		} catch (ScriptException e) {
			log.error("Error evaluating script.", e);
		} catch (NoSuchMethodException e) {
			log.error("The method " + eventName + " was not found.", e);
		} catch (IOException e) {
			log.error("Error loading file.", e);
		} finally {
			IOUtils.closeQuietly(reader);
		}

		log.info("end [script=" + inputFile + "; engine=" + engineName + "]");
	}
}